﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.IO;

namespace Chip8
{
    public partial class frmDebugger : Form
    {
        public frmDebugger()
        {
            InitializeComponent();
        }
       
       public struct Instruction //Instruction Breakdown
        {
            public ushort raw;
            public ushort opnum;
            public ushort nnn;
            public byte n;
            public byte x;
            public byte y;
            public byte kk;
        }

       public ushort GetOpcodeMask(ushort instruction)
        {
            ushort mask = 0xF000;
            byte opcode = (byte)((instruction & 0xF000) >> 12);
           
            switch (opcode)
            {
                case 0x0:
                    {
                        if (instruction > 0)
                        {
                            if ((instruction & 0xFFF0) == 0x00C0)
                            {
                                mask = 0xFFF0;
                            }
                            else
                            {
                                mask = 0xFFFF;
                            }
                        }
                        break;
                    }
                case 0xF:
                case 0xE: mask = 0xF0FF; break;
                case 0x8: mask = 0xF00F; break;
                default: mask = 0xF000; break;
            }

            return mask;
        }
    

        public void BuildInstructionList(ushort address,frmEmu Emu, byte byte1, byte byte2)
        {
           
            this.Invoke((MethodInvoker)delegate
            {
             String Decoded = null;
                 switch (Emu.instr.raw & GetOpcodeMask(Emu.instr.raw))
                 {
                     case 0x00E0: //CLS - Clear the display.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  CLS" + Environment.NewLine;
                         break;
                     case 0x00EE: //RET - Decoded = from a subroutine. The interpreter sets the program counter to the address at the top of the stack, then subtracts 1 from the stack pointer.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  RET" + Environment.NewLine;
                         break;
                     case 0x1000: //JP - Jump to location nnn. The interpreter sets the program counter to nnn.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  JP " + Emu.instr.nnn.ToString("X4") + Environment.NewLine;
                         break;
                     case 0x2000: //JSR - Push current PC to stack, and set PC = nnn.         
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  CALL " + Emu.instr.nnn.ToString("X4") + Environment.NewLine;
                         break;
                     case 0x3000: //SE - Skip next instruction if Vx = kk. The interpreter compares register Vx to kk, and if they are equal, increments the program counter by 2.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  SE V[" + Emu.instr.x.ToString("X") + "]," + Emu.instr.kk.ToString("X2") + Environment.NewLine;
                         break;
                     case 0x4000: //SNE - Skip next instruction if Vx != kk. The interpreter compares register Vx to kk, and if they are not equal, increments the program counter by 2. 
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  SNE V[" + Emu.instr.x.ToString("X") + "]," + Emu.instr.kk.ToString("X2") + Environment.NewLine;
                         break;
                     case 0x5000: //SE - Skip next instruction if Vx = Vy. The interpreter compares register Vx to register Vy, and if they are equal, increments the program counter by 2.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  SE V[" + Emu.instr.x.ToString("X") + "],V[" + Emu.instr.y.ToString("X") + "]" + Environment.NewLine;
                         break;
                     case 0x6000: //LD Set Vx = kk. The interpreter puts the value kk into register Vx.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  LD V[" + Emu.instr.x.ToString("X") + "]," + Emu.instr.kk.ToString("X2") + Environment.NewLine;
                         break;
                     case 0x7000: //ADD Set Vx = Vx + kk. Adds the value kk to the value of register Vx, then stores the result in Vx.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  ADD V[" + Emu.instr.x.ToString("X") + "], " + Emu.instr.kk.ToString("X2") + Environment.NewLine;
                         break;
                     case 0x8000: //LD - Set Vx = Vy. Stores the value of register Vy in register Vx.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  LD V[" + Emu.instr.x.ToString("X") + "], V[" + Emu.instr.y.ToString("X") + "]" + Environment.NewLine;
                         break;
                     case 0x8001: //OR -Set Vx = Vx OR Vy.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  OR V[" + Emu.instr.x.ToString("X") + "], V[" + Emu.instr.y.ToString("X") + "] | V[" + Emu.instr.y.ToString("X") + "]" + Environment.NewLine;
                         break;
                     case 0x8002: //AND - Set Vx = Vx AND Vy.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  AND V[" + Emu.instr.x.ToString("X") + "], V[" + Emu.instr.y.ToString("X") + "] & V[" + Emu.instr.y.ToString("X") + "]" + Environment.NewLine;
                         break;
                     case 0x8003: //XOR - Set Vx = Vx XOR Vy.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  XOR V[" + Emu.instr.x.ToString("X") + "], V[" + Emu.instr.y.ToString("X") + "] ^ V[" + Emu.instr.y.ToString("X") + "]" + Environment.NewLine;
                         break;
                     case 0x8004: //ADD - Set Vx = Vx + Vy, set VF = carry.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  ADD V[" + Emu.instr.x.ToString("X") + "], V[" + Emu.instr.y.ToString("X") + "]" + Environment.NewLine;
                         break;
                     case 0x8005: //SUB - Set Vx = Vx - Vy, set VF = NOT borrow.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  SUB V[" + Emu.instr.x.ToString("X") + "], V[" + Emu.instr.y.ToString("X") + "]" + Environment.NewLine;
                         break;
                     case 0x8006: //SHR - Set Vx = Vx SHR 1.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  SHR V[" + Emu.instr.x.ToString("X") + "] {, V[" + Emu.instr.y.ToString("X") + "]}" + Environment.NewLine;
                         break;
                     case 0x8007: //SUBN - Set Vx = Vy - Vx, set VF = NOT borrow.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  SUBN V[" + Emu.instr.x.ToString("X") + "], V[" + Emu.instr.y.ToString("X") + "]" + Environment.NewLine;
                         break;
                     case 0x800E: //SHL - Set Vx = Vx SHL 1.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  SHR V[" + Emu.instr.x.ToString("X") + "] {, V[" + Emu.instr.y.ToString("X") + "]}" + Environment.NewLine;
                         break;
                     case 0x9000: //SNE - Skip next instruction if Vx != Vy.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  SNE V[" + Emu.instr.x.ToString("X") + "], V[" + Emu.instr.y.ToString("X") + "]" + Environment.NewLine;
                         break;
                     case 0xA000: //LD - Set I = nnn.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  LD I," + Emu.instr.nnn.ToString("X4") + Environment.NewLine;
                         break;
                     case 0xB000: //JMP - Jump to location nnn + V0.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  JP " + "V[0]," + Emu.instr.nnn.ToString("X4") + Environment.NewLine;
                         break;
                     case 0xC000: //RND - Set Vx = random byte AND kk.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  RND " + "V[" + Emu.instr.x.ToString("X2") + "], RND(byte) & " + Emu.instr.kk.ToString("X2") + Environment.NewLine;
                         break;
                     case 0xD000: //DRW - Draw Screen.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  DRW" + Environment.NewLine;
                         break;
                     case 0xE09E: //SKP - Skip next instruction if key with the value of Vx is pressed.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  SKP " + "V[" + Emu.instr.x.ToString("X2") + "]"  + Environment.NewLine;
                         break;
                     case 0xE0A1: //SKNP - Skip next instruction if key with the value of Vx is not pressed.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  SKNP " + "V[" + Emu.instr.x.ToString("X2") + "]" + Environment.NewLine;
                         break;
                     case 0xF007: //LD - Set Vx = delay timer value.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  LD " + "V[" + Emu.instr.x.ToString("X2") + "], DT" + Environment.NewLine;
                         break;
                     case 0xF00A: //LD - Wait for a key press, store the value of the key in Vx.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  LD " + "V[" + Emu.instr.x.ToString("X2") + "]," + Emu.instr.kk.ToString("X2") + Environment.NewLine;
                         break;
                     case 0xF015: //LD - Set delay timer = Vx.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  LD " + "DT, V[" + Emu.instr.x.ToString("X2") + "]" + Environment.NewLine;
                         break;
                     case 0xF018: //LD - Set sound timer = Vx.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  LD " + "ST, V[" + Emu.instr.x.ToString("X2") + "]" + Environment.NewLine;
                         break;
                     case 0xF01E: //ADD - Set I = I + Vx.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  ADD " + "I, V[" + Emu.instr.x.ToString("X2") + "]" + Environment.NewLine;
                         break;
                     case 0xF029: //LD - Set I = location of sprite for digit Vx.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  LD " + "F, V[" + Emu.instr.x.ToString("X2") + "]" + Environment.NewLine;
                         break;
                     case 0xF033: //LD - Store BCD representation of Vx in memory locations I, I+1, and I+2.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  LD " + "B, V[" + Emu.instr.x.ToString("X2") + "]" + Environment.NewLine;
                         break;
                     case 0xF055: //LD - Store registers V0 through Vx in memory starting at location I.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  LD " + "[I], V[" + Emu.instr.x.ToString("X2") + "]" + Environment.NewLine;
                         break;
                     case 0xF065: //LD - Read registers V0 through Vx from memory starting at location I.
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  LD " + "V[" + Emu.instr.x.ToString("X2") + "], [I]" + Environment.NewLine;
                         break;
                     case 0xF075:
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  LD " + "R, V[" + Emu.instr.x.ToString("X2") + "]" + Environment.NewLine;       
                         break;
                     case 0xF085:
                         Decoded = address.ToString("X4") + "  " + byte1.ToString("X2") + " " + byte2.ToString("X2") + "  LD " + "V[" + Emu.instr.x.ToString("X2") + "], R" + Environment.NewLine;
                         break;
                     default:
                         Decoded = "0" + Environment.NewLine;
                         break;
                    }
                 if (chkLogCPU.Checked)
                 {
                     StreamWriter s = new StreamWriter("C:\\" + Emu.RomName + ".txt", true);
                     s.WriteLine(Decoded.Replace(Environment.NewLine, ""));
                     s.Close();
                 }
            });
            
        }
    
        private void frmDebugger_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = true;
            this.Hide();
        }
    
        private void frmDebugger_Load(object sender, EventArgs e)
        {

        }
    }
}
